#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>

#include "model.h"
#include "controller_manDaifu.h"
#include "controller_bstPhuc.h"
#include "UserIO.h"
#include "lamHash.h"

#define MAX_STR 100
#define FLUSH   while(getchar() != '\n')
#define NORMAL  FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
#define RED     FOREGROUND_RED

/* Create hash, bst struct base on the input file                    */
/* Pre:   hash - empty hash, bst - empty bst, arr_size - size of hash*/
/* Post:   hash, bst                                                 */
/* Return: none                                                      */
/* Written by: Daifu Ye                                              */
void buildManager (HASH **hash, BST **bst, STACK **stack, int arr_size)
{
	//Local Definitions
	char tempString[MAX_STR];
	FILE *infile;
	SOLDIER *data;
	
	//Statement
	infile = readFile("r");
	*hash = buildHash(arr_size);
	*bst = createBST(compare);
	*stack = createStack();
	
	// Store
	while(fgets(tempString, MAX_STR, infile))
	{
		data = storeToSOLFcn(tempString);
		insertBST(*bst, data);
		insertHash(*hash, data->name, data, arr_size);
	}// while
	
	fclose(infile);
	
	return;
}

/* process all the functions base on user's input                    */
/* Pre:  headNode, arr_size                                          */
/* Post:   none                                                      */
/* Return: none                                                      */
/* Written by: Daifu Ye & Faith Or (valid choice portion)            */
void processManager   (HEAD_NODE *headNode, int arr_size)
{
	//Local Definitions
	char option[5];
	int count;
	HANDLE hCon;
	
	//Statement
	hCon = GetStdHandle(STD_OUTPUT_HANDLE);
	
	do{
		menuOption();
		
		SetConsoleTextAttribute(hCon, 15);
		scanf("%4[^\n]", option);
		FLUSH;
		count = strlen(option);
		
		if(count > 1)
			option[0] = 'N';
		else
			option[0] = toupper(option[0]);

		switch(option[0]){
			case 'A' : insertManager(headNode->pHash, 
									headNode->pTree, 
									arr_size); 
						break;
			case 'D' : deleteManager(headNode->pHash, 
									headNode->pTree, 
									headNode->pStack, 
									arr_size); 
						break;
			case 'S' : searchManager(headNode->pHash, 
									headNode->pTree, 
									arr_size); 
						break; 
			case 'H' : listHashManager(headNode->pHash, 
									headNode->pTree, 
									arr_size); 
						break; 
			case 'K' : listBstManager(headNode->pTree); break;
			case 'P' : printIndentedBST(headNode->pTree, 
										printKey, 
										printBlank); 
						break;
			case 'W' : saveToFileManager(headNode->pTree, 
										headNode->pStack); 
						break;
			case 'E' : effciencyManager(headNode->pHash, 
										arr_size); 
						break;
			case 'U' : undoDelManager(headNode->pStack, 
									headNode->pTree, 
									headNode->pHash, 
									arr_size); 
						break;
			case 'Q' :  break;
			case 'N':
			default : SetConsoleTextAttribute(hCon, 78);
				printf("Invalid Choice Please try again.");
				SetConsoleTextAttribute(hCon, 15);
				printf("\n");
				option[0] = 'N';
				break;
		}
	} while(option[0] == 'N' || option[0] != 'Q');
	
	return;
}

/* Read user's input and insert into the database                    */
/* Pre:  hash, bst, arr_size                                         */
/* Post:   hash, bst point to a new SOLDIER data                     */
/* Return: none                                                      */
/* Written by: Daifu Ye & Faith Or (key verification)                */
void insertManager (HASH *hash, BST *bst, int arr_size)
{
	//Local Definitions
	char bufName[MAX_STR];
	int bufExp;
	char rank[MAX_STR];
	char div[MAX_STR];
	char jobCat[MAX_STR];
	SOLDIER *pSoldier;
	int targetIndex=0;
	int result=0;
	HANDLE hCon;
	
	//Statements
	hCon = GetStdHandle(STD_OUTPUT_HANDLE);
	printf("Please enter the soldier's information: \n");
	printf("Name: ");
	VerValidChr(bufName,1);
	result = checkEx(bufName, hash, &targetIndex ,arr_size);

	if(result){
		SetConsoleTextAttribute(hCon, 12);
		printf("%s already exists! Try to add a different person.",hash[targetIndex].pSoldier->name);
		SetConsoleTextAttribute(hCon, 15);
		return;
	}
			
	printf("Experience: ");
	bufExp = VerValidNum();
	printf("Rank: ");
	VerValidChr(rank,1);
	printf("Division: ");
	VerValidChr(div,1);
	printf("Job Category: ");
	VerValidChr(jobCat,0);
	pSoldier = createSoldier(bufName, bufExp, rank, div, jobCat);
	
	insertBST(bst, pSoldier);
	insertHash(hash, bufName, pSoldier, arr_size);
	SetConsoleTextAttribute(hCon, 14);
	printf("%s has been successfully inserted into the database.\n", pSoldier->name);
	SetConsoleTextAttribute(hCon, 15);
	return;
}

/* delete the data input by users                                    */
/* Pre:  hash, bst, stack, arr_size                                  */
/* Post:   hash, bst - reduce one SOLDIER; stack store deleted data  */
/* Return: none                                                      */
/* Written by: Daifu Ye & Faith Or (key verification)                */
void deleteManager (HASH *hash,  BST *bst, STACK *stack, int arr_size)
{
	//Local Definitions
	char bufName[MAX_STR];
	int deleteIndex;
	SOLDIER *dataOut;
	SOLDIER bufSoldier;
	HANDLE hCon;
	
	//Statements
	hCon = GetStdHandle(STD_OUTPUT_HANDLE);

	if(emptyBST(bst)){
		SetConsoleTextAttribute(hCon, 78);
		printf("There's nothing to delete!");
		SetConsoleTextAttribute(hCon, 10);
		return;
	}

	printf("Please enter the name you want to delete: ");
	VerValidChr(bufName,1);

	bufSoldier.name = allocateString(bufName);
	
	if(deleteHash(hash, bufName, &deleteIndex, (void **)&dataOut, arr_size) && deleteBST(bst, &bufSoldier, (void **) &dataOut)){
		SetConsoleTextAttribute(hCon, 14);
		printf("Successfully deleted soldier at : %d\n", deleteIndex);
		pushStack(stack, dataOut);
		printf("Soldier's data is saved temporarily in the stack.\n");
		SetConsoleTextAttribute(hCon, 15);
	}else {
		SetConsoleTextAttribute(hCon, 78);
		printf("Sorry, %s does not exist!",bufSoldier.name);
	}
	
	free(bufSoldier.name);
	return;
}

/* print out the efficiency data: load factor, collisions and its path*/
/* Pre:  hash, arr_size                                               */
/* Post:   none                                                       */
/* Return: none                                                       */
/* Written by: Daifu Ye                                               */
void effciencyManager(HASH *hash, int arr_size)
{
	//Local Definitions
	double loadFactor;
	int    collisionCount;
	int    longestCol;
	HANDLE hCon;
	
	//Statements
	hCon = GetStdHandle(STD_OUTPUT_HANDLE);

	getEfficiency(hash, &loadFactor, &collisionCount, &longestCol, arr_size);
	SetConsoleTextAttribute(hCon, 14);
	printf("\nLoad Factor: %4.2f%%", loadFactor);
	printf("\n%d data records have collisions", collisionCount);
	printf("\nLongest collision Path: %d\n", longestCol);
	SetConsoleTextAttribute(hCon, 15);

	return;
}

/* Save the data from database to a file and clean stack             */
/* Pre:  bst, stack                                                  */
/* Post:   stack - become empty                                      */
/* Return: none                                                      */
/* Written by: Daifu Ye                                              */
void saveToFileManager(BST *bst, STACK *stack)
{
	HANDLE hCon;

	//Statements
	hCon = GetStdHandle(STD_OUTPUT_HANDLE);
	if(emptyBST(bst)){
		SetConsoleTextAttribute(hCon, 78);
		printf("CAUTION: This will be an empty file!");
		SetConsoleTextAttribute(hCon, 10);
	}
	
	printf("\n");
	printToFile(bst);
	ClearStack(stack);
	SetConsoleTextAttribute(hCon, 14);
	printf("\nThe file has been successfully saved.\n");

	return;
}

/* clear out all the memory inside the database                      */
/* Pre:  headNode, arr_size                                          */
/* Post:   clear all the memory in the heap                          */
/* Return: none                                                      */
/* Written by: Daifu Ye                                              */
void memoryManager(HEAD_NODE *headNode)
{
	//Statements
	destroyStack(headNode->pStack, freeStackMemory);
	printToFile(headNode->pTree);
	destroyBST(headNode->pTree, freeMem);
	free(headNode->pHash);
	free(headNode);
	return;
}

/* free stack memory                                                 */
/* Pre:  dataOut - SOLDIER data pointer                              */
/* Post:   free dataOut memory                                       */
/* Return: none                                                      */
/* Written by: Daifu Ye                                              */
void freeStackMemory(void* dataOut)
{
	SOLDIER *data = (SOLDIER *) dataOut;
	
	free(data->division);
	free(data->jobCat);
	free(data->name);
	free(data->rank);
	
}//end freeMemory

/* Get the option form the user input                                */
/* Pre:    none                                                      */
/* Post:   option - store the user input                             */
/* Return: option - a option from user's input                       */
char getOption (void) {
	//Local Definitions 
	char option;

	//Statements 
	printf ("Do you want animation? (Y/N): ");
	scanf  ("%c", &option);
	FLUSH;
	option = toupper(option);
	while(strchr("YN", option) == NULL)
	{
		printf("\a*** Invalid option! ***\n");
		printf("Enter one of the following letters: Y, N: " );
		scanf  ("%c", &option);
		FLUSH;
		option = toupper(option);
	}

	return option;
}